#include "connection.hpp"
#include "object_pool.hpp"
#include "fixed_size_allocator.hpp"

#include "conn_manager.hpp"

namespace tcp
{
	ConnManager::ConnManager(int max_conn, FixedSizeAllocator * alloc,
			long epoll_timeout)
		: _epoll_timeout(epoll_timeout),
		  _simple_epoll(max_conn),
		  _alloc(alloc),
		  _conn_pool(max_conn),
		  _conn_id(0)
	{
	}

	ConnManager::~ConnManager()
	{
		for (ConnMap_Iter iter = _conn_map.begin(); iter != _conn_map.end(); ++ iter)
		{
			_conn_pool.destroy(iter->second);
		}

		_conn_map.clear();
	}

	Connection * ConnManager::allocate(const SockAddr & addr, int fd)
	{
		Connection * conn = _conn_pool.construct(*_alloc);
		
		assert(NULL != conn);

		conn->_fd         = fd;
		conn->_conn_mgr   = this;
		conn->_sock_addr  = addr;

		_conn_map.insert(std::make_pair(fd, conn));

		assert(0 == _simple_epoll.ctl_add(conn->fd(), EPOLL_EVENT));

		return conn;
	}

	void ConnManager::deallocate(Connection * conn)
	{
		_conn_map.erase(conn->fd());
		_conn_pool.destroy(conn);
	}

	int ConnManager::add_acceptor(const char * host, int port, 
			IDatagramFactory * factory, IProcessor * processor)
	{
		Connection * conn = _conn_pool.construct(*_alloc);

		assert(NULL != conn);

		conn->_sock_addr   = SockAddr(host, port);
		conn->_conn_mgr    = this;
		conn->_factory     = factory;
		conn->_processor   = processor;
		
		conn->conn_id(next_id());
		conn->listen_only(true);

		assert(conn->listen() == 0);
		assert(0 == _simple_epoll.ctl_add(conn->fd(), EPOLL_EVENT));

		_conn_map.insert(std::make_pair(conn->fd(), conn));

		return 0;
	}

	int ConnManager::add_connector(const char * host, int port,
			IDatagramFactory * factory, IProcessor * processor)
	{
		Connection * conn = _conn_pool.construct(*_alloc);

		assert(NULL != conn);

		conn->_sock_addr   = SockAddr(host, port);
		conn->_conn_mgr    = this;
		conn->_factory     = factory;
		conn->_processor   = processor;
		
		conn->conn_id(next_id());
		conn->listen_only(false);

		assert(conn->connect() == 0);
		assert(0 == _simple_epoll.ctl_add(conn->fd(), EPOLL_EVENT));

		_conn_map.insert(std::make_pair(conn->fd(), conn));

		return 0;
	}

	int ConnManager::poll()
	{
		int ret = _simple_epoll.wait(_epoll_timeout);
		
		if (ret <= 0)
		{
			return ret;
		}

		while (true)
		{
			int fd;
			unsigned int op;
			
			if (-1 == _simple_epoll.next(fd, op))
			{
				break;
			}
			
			ConnMap_Iter iter = _conn_map.find(fd);
			if (_conn_map.end() == iter)
			{
				continue;
			}

			Connection * conn = iter->second;

			if ((op & (EPOLLIN | EPOLLHUP)) != 0)
			{
				conn->read();
			}

			if ((op & EPOLLOUT) != 0)
			{
				conn->write();
			}

			if ((op & EPOLLERR) != 0)
			{
				conn->error();
			}
		}

		return 0;
	}
};

